package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.entity.*;
import cn.mw.cmdb.mongoMapper.ModelPropertySortMapper;
import cn.mw.cmdb.mongoMapper.TableHeaderMapper;
import cn.mw.cmdb.param.TableSearchParam;
import cn.mw.cmdb.processor.TableDataFactory;
import cn.mw.cmdb.processor.TableDataProccessor;
import cn.mw.cmdb.processor.TableDataProcessorContext;
import cn.mw.cmdb.processor.TreeProccessorTable;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.view.StaticsViewInstance;
import cn.mw.cmdb.view.StaticsViewModel;
import cn.mw.cmdb.view.ViewConvertContext;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.api.cmdb.TableIdEnum;
import cn.mw.microMonitorCommon.api.cmdb.TreeView;
import cn.mw.microMonitorCommon.api.common.MwEngineManageService;
import cn.mw.microMonitorCommon.api.common.MwTPServerService;
import cn.mw.microMonitorCommon.api.user.MwUserApi;
import cn.mw.microMonitorCommon.entity.cmdb.TableHeaderItem;
import cn.mw.microMonitorCommon.security.utils.SecurityUtils;
import cn.mw.microMonitorCommon.util.SpringUtils;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static cn.mw.cmdb.enums.ModelConfigKeyEnum.sortCommonField;
import static cn.mw.cmdb.util.ListSortUtil.sortListByTypeOrder;
import static cn.mw.cmdb.util.ValConvertUtil.strValueConvert;

@Service
@Slf4j
public class TableHeaderAndDataManage implements InitializingBean {

    private CmdbServiceManage cmdbServiceManage;

    private TableHeaderMapper tableHeaderMapper;

    private Map<TableIdEnum, Class> tableIdEnumMap;
    private static final String MODEL_ID = "modelId";
    @DubboReference(check = false, mock = "true", timeout = 15000)
    private MwUserApi mwUserApi;

    @Value("#{'${cmdb.tableHeader.sort}'.split(',')}")
    private List<String> tableHeaderSort;
    @DubboReference(check = false, mock = "true", timeout = 15000)
    private MwTPServerService mwTPServerService;
    @DubboReference(check = false, mock = "true", timeout = 15000)
    private MwEngineManageService mwEngineManageService;

    public TableHeaderAndDataManage(TableHeaderMapper tableHeaderMapper, CmdbServiceManage cmdbServiceManage) {
        this.tableHeaderMapper = tableHeaderMapper;
        this.cmdbServiceManage = cmdbServiceManage;
    }

    public List<TableHeaderItem> listHeaders(TableSearchParam param) throws Exception {
        String tableIdStr = param.getTableId();
        if (!StringUtils.hasText(tableIdStr)) {
            return null;
        }
        TableIdEnum tableId = TableIdEnum.getTableIdEnum(tableIdStr);
        Long userId = SecurityUtils.getUserId();
        List<Criteria> criteriaList = new ArrayList<>();
        String modelId = "";
        //当tableId为customInstance、instance时，表示需要获取自定义的查询数据，需要带上id，此时id表示为modelId
        if (TableIdEnum.customInstance.getCode().equals(tableIdStr) || TableIdEnum.instance.getCode().equals(tableIdStr)) {
            modelId = param.getId();
            criteriaList.add(CriteriaWrapper.where(TableHeader::getModelId).is(String.valueOf(param.getId())));
        }
        criteriaList.add(CriteriaWrapper.where(TableHeader::getUserId).is(String.valueOf(userId)));
        criteriaList.add(CriteriaWrapper.where(TableHeader::getTableId).is(tableIdStr));
        criteriaList.add(CriteriaWrapper.where(TableHeader::getMenuId).is(param.getMenuId()));
        //自定义表头数据 + 模型中列表有展示权限的属性
        Query query = new Query().addCriteria(new Criteria().andOperator(criteriaList));
        TableHeader tableHeader = tableHeaderMapper.findOne(query);
        List<TableHeaderItem> items = new ArrayList<>();
        if (tableHeader != null) {
            items = tableHeader.getItems();
            items = items.stream().filter(s -> !Strings.isNullOrEmpty(s.getHeaderName())).collect(Collectors.toList());
        }
        TableDataProcessorContext context = new TableDataProcessorContext();
        context.setTableSearchParam(param);
        context.add(MODEL_ID, modelId);
        Class clazz = tableIdEnumMap.get(tableId);
        List<TableHeaderItem> defaultTableHeaderItem = getDefaultTableHeaderItem(clazz, context);
        if (CollectionUtils.isEmpty(items)) {
            return defaultTableHeaderItem;
        }
        //表头数据校验
        List<TableHeaderItem> tableHeaderItems = mergeLists(items, defaultTableHeaderItem);
        return tableHeaderItems;
    }

    /**
     * 清除表头自定义设置
     *
     * @return
     */
    public boolean cleanTableHeader() {
        Query query = new Query();
        tableHeaderMapper.remove(query);
        return true;
    }

    /**
     * 合并去重
     *
     * @param customList  自定义表头数据
     * @param defaultList 默认表头数据
     */
    public static List<TableHeaderItem> mergeLists(List<TableHeaderItem> customList, List<TableHeaderItem> defaultList) {
        Map<String, TableHeaderItem> propertyMapById = customList.stream().collect(Collectors.toMap(s -> s.getField(), s -> s, (
                value1, value2) -> {
            return value2;
        }));

        List<TableHeaderItem> list = new ArrayList<>();
        for (TableHeaderItem tableHeaderItem : defaultList) {
            String fieldId = tableHeaderItem.getField();
            if (propertyMapById != null) {
                if (propertyMapById.containsKey(fieldId)) {
                    TableHeaderItem tableHeaderByProperty = propertyMapById.get(fieldId);
                    tableHeaderItem.setVisible(tableHeaderByProperty.getVisible());
                    tableHeaderItem.setFlex(tableHeaderByProperty.getFlex());
                }
                list.add(tableHeaderItem);
            }
        }
        Map<String, Integer> indexMap = new HashMap<>();
        for (int i = 0; i < customList.size(); i++) {
            indexMap.put(customList.get(i).getField(), i);
        }
        Collections.sort(list, new Comparator<TableHeaderItem>() {
            @Override
            public int compare(TableHeaderItem bean1, TableHeaderItem bean2) {
                Integer index1 = indexMap.get(bean1.getField());
                Integer index2 = indexMap.get(bean2.getField());
                if (index1 != null && index2 != null) {
                    return Integer.compare(index1, index2);
                } else if (index1 != null) {
                    return -1; // bean1在List<String>中存在，所以排在前面
                } else if (index2 != null) {
                    return 1; // bean2在List<String>中存在，所以排在前面
                } else {
                    return 0; // bean1和bean2都不在List<String>中，保持原顺序
                }
            }
        });

        return list;
    }

    public List listTableData(TableSearchParam param) throws Exception {


        MwCMDBConfigImpl configServer = (MwCMDBConfigImpl) cmdbServiceManage.getModelService(MwCMDBConfigImpl.ID);
        MwCMDBConfigInfo cmdbConfigInfo = configServer.doSelectByKey(sortCommonField.name());
        Object configValue = cmdbConfigInfo.getConfigValue();
        List<String> configValueList = new ArrayList<>();
        if (configValue instanceof List) {
            configValueList = (List) configValue;
        }
        if (CollectionUtils.isEmpty(configValueList)) {
            configValueList = tableHeaderSort;
        }
        TableDataProccessor proccessor = TableDataFactory.build(param, cmdbServiceManage, configValueList);
        List ret = null;
        if (null != proccessor) {
            TableDataProcessorContext context = new TableDataProcessorContext();
            context.add(ViewConvertContext.MwuserApiKey, mwUserApi);
            context.add(ViewConvertContext.MwEngineManageServiceKey, mwEngineManageService);
            context.add(ViewConvertContext.MwTPServerServiceKey, mwTPServerService);
            context.add(CmdbServiceManage.class.getName(), cmdbServiceManage);
            TableIdEnum tableId = TableIdEnum.getTableIdEnum(param.getTableId());
            Class clazz = tableIdEnumMap.get(tableId);
            long time1 = System.currentTimeMillis();
            List<TableHeaderItem> tableHeaderItems = proccessor.genDefaultTableHeader(clazz);
            long time2 = System.currentTimeMillis();

            context.setTableHeaderItems(tableHeaderItems);
            ret = proccessor.genData(context);
            long time3 = System.currentTimeMillis();
            log.info("获取实例列表表头耗时:" + (time2 - time1) + "ms;获取实例列表耗时:" + (time3 - time2) + "ms");

        }
        return ret;
    }

    public List<TreeView> listTree(TableSearchParam param) throws Exception {
        TreeProccessorTable proccessor = TableDataFactory.buildTree(param, cmdbServiceManage);
        List ret = null;
        TableDataProcessorContext context = new TableDataProcessorContext();
        context.add(ViewConvertContext.MwuserApiKey, mwUserApi);
        context.setTableSearchParam(param);
        if (null != proccessor) {
            ret = proccessor.genTreeData(context);
        }

        return ret;
    }


    private List<TableHeaderItem> getDefaultModelTableHeaderItem(String modelId) throws Exception {
        List<TableHeaderItem> tableHeaderItems = new ArrayList<>();
        //根据modelId获取当前模型所对应的全部属性
        CustomModelServiceImpl modelService = (CustomModelServiceImpl) cmdbServiceManage.getModelService(CustomModelServiceImpl.ID);
        ModelInfo modelInfo = modelService.doSelectById(modelId);
        //将属性按照显示权限过滤（勾选列表显示的），转换为List<TableHeaderItem>放回
        //按照属性信息来循环处理,保证只会输入模型中定义的属性
        for (PropertyInfo propertyInfo : modelInfo.getPropertyInfoList()) {
            Set<FieldViewAuthType> fieldViewAuthTypes = propertyInfo.getFieldViewAuthTypes();
            //获取所有列表显示的字段属性
            if (null != fieldViewAuthTypes && fieldViewAuthTypes.contains(FieldViewAuthType.list)) {
                TableHeaderItem item = new TableHeaderItem();
                item.setField(propertyInfo.getId());
                item.setHeaderName(propertyInfo.getName());
                item.setVisible(true);
                tableHeaderItems.add(item);
            }
        }
        return tableHeaderItems;
    }


    private List<TableHeaderItem> getDefaultTableHeaderItem(Class clazz, TableDataProcessorContext context) throws Exception {
        MwCMDBConfigImpl configServer = (MwCMDBConfigImpl) cmdbServiceManage.getModelService(MwCMDBConfigImpl.ID);
        MwCMDBConfigInfo cmdbConfigInfo = configServer.doSelectByKey(sortCommonField.name());
        Object configValue = cmdbConfigInfo.getConfigValue();
        List<String> configValueList = new ArrayList<>();
        if (configValue instanceof List) {
            configValueList = (List) configValue;
        }
        if (CollectionUtils.isEmpty(configValueList)) {
            configValueList = tableHeaderSort;
        }
        TableDataProccessor proccessor = TableDataFactory.build(context.getTableSearchParam(), cmdbServiceManage, configValueList);
        List<TableHeaderItem> list = proccessor.genDefaultTableHeader(clazz);
        if (context != null && context.get(MODEL_ID) != null) {
            String modelId = strValueConvert(context.get(MODEL_ID));
            Query query = new Query(CriteriaWrapper.where(ModelPropertySortInfo::getModelId).is(modelId));
            ModelPropertySortMapper modelPropertySortMapper = SpringUtils.getBean(ModelPropertySortMapper.class);
            ModelPropertySortInfo propertySortInfo = modelPropertySortMapper.findOne(query);
            if (propertySortInfo != null && CollectionUtils.isNotEmpty(propertySortInfo.getPropertyIds())) {
                List<String> propertyIds = propertySortInfo.getPropertyIds();
                sortListByTypeOrder(list, CMDBTool.getFieldName(TableHeaderItem::getField), propertyIds);
            }
        }
        return list;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        tableIdEnumMap = new HashMap<>();
        tableIdEnumMap.put(TableIdEnum.model, StaticsViewModel.class);
        tableIdEnumMap.put(TableIdEnum.instance, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.org, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.assetType, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.brand, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.group, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.userGroup, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.customInstance, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.business, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.commonInstance, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.custom, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.financeModel, StaticsViewInstance.class);
        tableIdEnumMap.put(TableIdEnum.finance, StaticsViewModel.class);
        tableIdEnumMap.put(TableIdEnum.businessModel, StaticsViewModel.class);
    }
}
